package Main;

/*
 *
 * Copyright (c) 2007, Sun Microsystems, Inc.
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *  * Neither the name of Sun Microsystems nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import java.io.IOException;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.media.*;
import javax.microedition.media.control.VideoControl;
import javax.microedition.amms.control.camera.*;
import java.util.Vector;
import java.util.Enumeration;
import javax.microedition.io.file.*;
import javax.microedition.io.*;


public class LAPSE extends MIDlet implements CommandListener, ItemStateListener
{
    private Player player;
    private CameraControl camCtrl = null;
    private VideoControl vidCtrl = null;
    private ExposureControl expCtrl = null;
    private FlashControl flsCtrl = null;
    private FocusControl fcsCtrl = null;
    private ZoomControl zoomCtrl = null;
    private SnapshotControl snapCtrl = null;

    private Command exitCommand = new Command("Exit", Command.EXIT, 2);
    private Command backCommand = new Command("Back", Command.BACK, 1);
    
    private final Command startShootingCommand = new Command("Start", Command.ITEM, 1);
    private final Command stopShootingCommand = new Command("Stop", Command.ITEM, 1);
    private final Command setTimerCommand = new Command("Set delay", Command.ITEM, 4);
    private final Command setStorageCommand = new Command("Set storage", Command.ITEM, 5);
    private final Command showTraceCommand = new Command("Show log", Command.ITEM, 6);
    private final Command setResolutionCommand = new Command("Set resolution", Command.ITEM, 7);
    private final Command changeExposureCommand = new Command("Choose exposure", Command.ITEM, 8);
    private final Command enabledShutterFeedbackCommand = new Command("Enable shutter FB", Command.ITEM, 9);
    private final Command disabledShutterFeedbackCommand = new Command("Disable shutter FB", Command.ITEM, 9);
    private final Command setSupportedF_StopsCommand = new Command("Change FStop", Command.ITEM, 10);
    private final Command setFlashModeCommand = new Command("Set flash mode", Command.ITEM, 11);
    private final Command setFocusCommand = new Command("Focus settings", Command.ITEM, 12);
    private final Command setZoomCommand = new Command("Zoom settings", Command.ITEM, 13);

    
    private Display display;
    private Form vidForm;
    private Item videoItem;
    private ChoiceGroup exposureModes;
    private Form exposureModesForm;
    private ChoiceGroup F_StopsCG;
    private Form supportedF_StopsForm;
    private ChoiceGroup flashModes;
    private ChoiceGroup focusModes;
    private ChoiceGroup zoomOptModes;
    private ChoiceGroup zoomDigModes;
    private Form flashModesForm;
    private Form focusModesForm;
    private Form zoomModesForm;
    private String focusSupportedModes[];
    private int focusModesInt[];
    private String zoomOptSupportedModes[];
    private int zoomOptModesInt[];
    private String zoomDigSupportedModes[];
    private int zoomDigModesInt[];
    
    private static String[] flashAllModes = {"OFF", "AUTO", "AUTO_WITH_REDEYEREDUCE", "FORCE", "FORCE_WITH_REDEYEREDUCE", "FILLIN" };

    private String PHOTOS_DIR = null;
    
    boolean shooting = false;
    int time = 10000;
    Form timerForm;
    ChoiceGroup times;
    Form resolutionForm;
    ChoiceGroup resolutions;
    String snapDir;
    
    private Form locationForm;
    private ChoiceGroup storageLocs;
    private Vector locs = new Vector();
    
    private Form traceForm;

    public LAPSE() 
    {
        //Set up trace form
        traceForm = new Form("LAPSE Log"); 
        display = Display.getDisplay(this);
        display.setCurrent(traceForm);
        
        // Create LAPSE default folder
        Enumeration roots = FileSystemRegistry.listRoots();
        String root = "";
        
        while(roots.hasMoreElements())
        {
            root = (String)roots.nextElement();
            locs.addElement(root);
            trace("Found storage root: " + root);
        }
        trace("Using: " + root);
        
        if(setupVideo())
        {
            setupOptions();
            trace("Initialisation complete");
            display.setCurrent(vidForm);
        }
        else
            trace("Fatal error: Cannot access camera, try LAPSE basic");
        traceForm.addCommand(backCommand);   
        traceForm.setCommandListener(this);                
    }
    
    private void setupOptions()
    {
        trace("Setting up exposure options...");
        exposureModesForm = new Form("Select exposure modes:");
        exposureModes = new ChoiceGroup(null, Choice.EXCLUSIVE, camCtrl.getSupportedExposureModes(), null);
        exposureModesForm.append(exposureModes);
        exposureModesForm.setItemStateListener(this);
        exposureModesForm.addCommand(backCommand);
        exposureModesForm.setCommandListener(this);
        
        trace("Setting up resolution options...");
        resolutionForm = new Form("Select resolution:");
        int resIntArray[] = camCtrl.getSupportedStillResolutions();
        String res[] = new String[resIntArray.length / 2];
        int i = 0, j = 0;
        while(i < res.length) 
        {
            res[i] = resIntArray[j] + "x" + resIntArray[j + 1];
            i++;j++;j++;
        }
        resolutions = new ChoiceGroup(null, Choice.EXCLUSIVE, res, null);
        resolutions.setSelectedIndex(0, true);
        resolutionForm.append(resolutions);
        resolutionForm.addCommand(backCommand);
        resolutionForm.setItemStateListener(this);
        resolutionForm.setCommandListener(this);
        
        trace("Setting up F-stop options...");
        supportedF_StopsForm = new Form("Select F_Stop number:");
        int fpIntArray[] = expCtrl.getSupportedFStops();
        String[] fpStops = new String[fpIntArray.length];
        for (int k = 0; k < fpIntArray.length; k++)
            fpStops[k] = Integer.toString(fpIntArray[k]);
        F_StopsCG = new ChoiceGroup(null, Choice.EXCLUSIVE, fpStops, null);
        supportedF_StopsForm.append(F_StopsCG);
        supportedF_StopsForm.setItemStateListener(this);
        supportedF_StopsForm.addCommand(backCommand);
        supportedF_StopsForm.setCommandListener(this);
        
        trace("Setting up focus options...");
        focusModesForm = new Form("Select focus mode:");                
        focusSupportedModes = new String[fcsCtrl.getFocusSteps() + 1];
        focusModesInt = new int[focusSupportedModes.length];
        focusSupportedModes[0] = "Auto Mode";
        focusModesInt[0] = fcsCtrl.AUTO;
        try
        {
            fcsCtrl.setFocus(fcsCtrl.getMinFocus());
            for (int l = 1; l < focusSupportedModes.length; l++) 
            {
                focusModesInt[l] = fcsCtrl.getFocus();
                focusSupportedModes[l] = Integer.toString(focusModesInt[l]);
                fcsCtrl.setFocus(fcsCtrl.NEXT);
            }
            if(focusModesInt[focusModesInt.length - 1] == Integer.MAX_VALUE) 
                focusSupportedModes[focusModesInt.length - 1] = "infinity";
            fcsCtrl.setFocus(fcsCtrl.AUTO);
        }
        catch(MediaException me)
        {
            traceErr(me);
        }
        //int initialFocusMode = flsCtrl.getMode();
        focusModes = new ChoiceGroup(null, Choice.EXCLUSIVE, focusSupportedModes, null);                                             
        focusModes.setSelectedIndex(0, true);
        focusModesForm.append(focusModes);
        focusModesForm.setItemStateListener(this);
        focusModesForm.addCommand(backCommand);
        focusModesForm.setCommandListener(this);
        
        trace("Setting up digital zoom options...");
        zoomModesForm = new Form("Select zoom mode:");  
        zoomDigSupportedModes = new String[zoomCtrl.getDigitalZoomLevels()];
        zoomDigModesInt = new int[zoomDigSupportedModes.length];
        zoomCtrl.setDigitalZoom(100);
        for (int m = 0; m < zoomDigSupportedModes.length; m++) 
        {                    
            zoomDigModesInt[m] = zoomCtrl.getDigitalZoom();
            zoomDigSupportedModes[m] = Integer.toString(zoomDigModesInt[m]);
            zoomCtrl.setDigitalZoom(zoomCtrl.NEXT);
        }
        zoomCtrl.setDigitalZoom(100);
        zoomDigModes = new ChoiceGroup(null, Choice.EXCLUSIVE, zoomDigSupportedModes, null);                                             
        zoomDigModes.setSelectedIndex(0, true);
        zoomModesForm.append("Set digital zoom");
        zoomModesForm.append(zoomDigModes);
        
        trace("Setting up optical zoom options...");
        zoomOptSupportedModes = new String[zoomCtrl.getOpticalZoomLevels()];
        zoomOptModesInt = new int[zoomOptSupportedModes.length];
        zoomCtrl.setOpticalZoom(100);
        for (int m = 0; m < zoomOptSupportedModes.length; m++) 
        {                    
            zoomOptModesInt[m] = zoomCtrl.getOpticalZoom();
            zoomOptSupportedModes[m] = Integer.toString(zoomOptModesInt[m]);
            zoomCtrl.setOpticalZoom(zoomCtrl.NEXT);
        }
        zoomOptModes = new ChoiceGroup(null, Choice.EXCLUSIVE, zoomOptSupportedModes, null);                                             
        zoomOptModes.setSelectedIndex(0, true);
        zoomModesForm.append("Set optical zoom");
        zoomModesForm.append(zoomOptModes);
        zoomModesForm.setItemStateListener(this);
        zoomModesForm.addCommand(backCommand);
        zoomModesForm.setCommandListener(this);
        
        trace("Setting up delay options...");
        timerForm = new Form("Select time delay:");
        String delays[] = {"1/2sec", "1sec", "2sec", "5sec", "10sec", "20sec", "30sec", "1min", "2min", "5min"};
        times = new ChoiceGroup(null, Choice.EXCLUSIVE, delays, null);
        times.setSelectedIndex(4, true);//When default is 10sec
        timerForm.append(times);
        timerForm.setItemStateListener(this);
        timerForm.setCommandListener(this);
        timerForm.addCommand(backCommand);
        
        trace("Setting up flash options...");
        flashModesForm = new Form("Select flash mode:");
        int flashIntArray[] = flsCtrl.getSupportedModes();                
        String flashSupportedModes[] = new String[flashIntArray.length];
        
        for (int n = 0; n < flashIntArray.length; n++)
            flashSupportedModes[n] = flashAllModes[n];
        
        int initialFlashMode = flsCtrl.getMode();
        flashModes = new ChoiceGroup(null, Choice.EXCLUSIVE, flashSupportedModes, null);
        flashModes.setSelectedIndex((initialFlashMode - 1), true);
        flashModesForm.append(flashModes);
        flashModesForm.setItemStateListener(this);
        flashModesForm.addCommand(backCommand);
        flashModesForm.setCommandListener(this);
        
        trace("Setting up storage options...");
        String locsArr[] = new String[locs.size()];
        locs.copyInto(locsArr);
        locationForm = new Form("Select storage location:");
        storageLocs = new ChoiceGroup(null, Choice.EXCLUSIVE, locsArr, null);
        locationForm.append(storageLocs);
        locationForm.setItemStateListener(this);
        locationForm.setCommandListener(this);
        locationForm.addCommand(backCommand);   
    }
    
    private boolean setupVideo()
    {
        trace("Setting up video...");
        vidForm = new Form("LAPSE");
        vidForm.setCommandListener(this);
        
        try 
        {           
            player = Manager.createPlayer("capture://video");
            player.prefetch();
            player.realize();
            
            vidCtrl = (VideoControl)player.getControl("VideoControl");
            if (vidCtrl != null) 
            {
                videoItem = (Item)vidCtrl.initDisplayMode(VideoControl.USE_GUI_PRIMITIVE, null);
                vidCtrl.setDisplaySize(vidForm.getWidth(), vidForm.getHeight());
            } 
            else 
                return false;
            
            camCtrl = (CameraControl)player.getControl("javax.microedition.amms.control.camera.CameraControl");
            if (camCtrl != null) 
            {
                vidForm.addCommand(setResolutionCommand);
                vidForm.addCommand(setTimerCommand);
                vidForm.addCommand(changeExposureCommand);
                vidForm.addCommand(disabledShutterFeedbackCommand);
            }
            else 
                return false;
            
            expCtrl = (ExposureControl)player.getControl("javax.microedition.amms.control.camera.ExposureControl");
            if (expCtrl != null)
                vidForm.addCommand(setSupportedF_StopsCommand);
            flsCtrl = (FlashControl)player.getControl("javax.microedition.amms.control.camera.FlashControl");
            if (flsCtrl != null)
                vidForm.addCommand(setFlashModeCommand);
            fcsCtrl = (FocusControl)player.getControl("javax.microedition.amms.control.camera.FocusControl");
            if (fcsCtrl != null)
                vidForm.addCommand(setFocusCommand);
            zoomCtrl = (ZoomControl)player.getControl("javax.microedition.amms.control.camera.ZoomControl"); 
            if (zoomCtrl != null)
                vidForm.addCommand(setZoomCommand);
            
            snapCtrl = (SnapshotControl)player.getControl("javax.microedition.amms.control.camera.SnapshotControl");
            
            if (snapCtrl != null) 
            {
                String loc = (String)(locs.elementAt(0));
                trace("Setting storage location: " + loc);
                createPhotoDir(loc);   
                trace("Setting file prefix to lapse");
                snapCtrl.setFilePrefix("");
                trace("Setting file suffix to .jpg");
                snapCtrl.setFileSuffix(".jpg");
                trace("Setting file folder to: " + snapDir);
                snapCtrl.setDirectory("/" + snapDir);  
            }
            else 
            {
                trace("Error: Can not create SnapshotControl. Please try LAPSE Basic");
                return false;
            }
        }
        catch (Exception e) 
        {
            traceErr(e);
        }
        
        vidForm.append(videoItem);
        vidForm.addCommand(startShootingCommand);
        vidForm.addCommand(setStorageCommand);
        vidForm.addCommand(showTraceCommand);
        vidForm.addCommand(exitCommand);
        return true;
    }
    
    private void trace(String trace)
    {
        System.out.println(trace);
        traceForm.append(trace + "\n");
    }
    
    private void traceErr(Exception err)
    {
        err.printStackTrace();
        trace("Error:" + err.getMessage());
    } 
    
    void createPhotoDir(String loc)
    {
        String folders[] = {"", "Pictures/", "Images/"}; 
        boolean success = false;

        for(int i = 0; i < folders.length; i++)    
        {
            if(success)
                break;

            try 
            {
                snapDir = loc + folders[i] + "LAPSE/";
                trace("File folder: " + snapDir);         
                PHOTOS_DIR = "file:///" + snapDir ;
                createDirByName(PHOTOS_DIR);
                success = true;
            }
            catch(Exception ioe) 
            {
                trace("Failed to create folder:\n" + PHOTOS_DIR);
                traceErr(ioe);   
            }                
        }
    }
    
    private void createDirByName(String dir) throws IOException
    {
            FileConnection fconn = (FileConnection)Connector.open(dir);
            if (!fconn.exists())
            {
                trace("Creating folder:");
                trace(dir);
                fconn.mkdir();
                trace("Setting folder writeable");
                fconn.setWritable(true);
                trace("Setting folder readable");
                fconn.setReadable(true);
                trace("Folder created");
            }
            else
                trace("Folder already exists");
    }

    public void startApp() 
    {
        try 
        {
            player.start();
        } 
        catch (MediaException me) 
        {
            me.printStackTrace();
        }
    }

    public void pauseApp() 
    {
        if (camCtrl != null) 
            startStopShooting();

        try 
        {
            player.stop();
        } 
        catch (MediaException me) 
        {
            me.printStackTrace();
        }
    }

    public void destroyApp(boolean unconditional) 
    {
        closePlayer();
        notifyDestroyed();
    }

    /*
     * Respond to commands, including back
     */
    public void commandAction(Command c, Displayable s) 
    {
        if (c == backCommand) 
           display.setCurrent(vidForm);
        else if (s == vidForm) 
        {
            if (c == exitCommand) 
                destroyApp(false);
            else if (c == startShootingCommand) 
                startStopShooting();
            else if (camCtrl != null && c == enabledShutterFeedbackCommand) 
                enabledShutterFeedback(true);
            else if (camCtrl != null && c == disabledShutterFeedbackCommand) 
                enabledShutterFeedback(false);
            else if (camCtrl != null && c == changeExposureCommand) 
                display.setCurrent(exposureModesForm);
            else if (camCtrl != null && c == setSupportedF_StopsCommand) 
                display.setCurrent(supportedF_StopsForm);
            else if (camCtrl != null && c == setFlashModeCommand) 
                display.setCurrent(flashModesForm);
            else if (camCtrl != null && c == setResolutionCommand) 
                display.setCurrent(resolutionForm);
            else if (camCtrl != null && c == setTimerCommand) 
                display.setCurrent(timerForm);
            else if (camCtrl != null && c == stopShootingCommand) 
                startStopShooting();
            else if (camCtrl != null && c == setFocusCommand) 
                display.setCurrent(focusModesForm);
            else if (camCtrl != null && c == setZoomCommand) 
                display.setCurrent(zoomModesForm);
            else if (c == showTraceCommand) 
                display.setCurrent(traceForm);
            else if (c == setStorageCommand) 
                display.setCurrent(locationForm);
        } 
    }
      
    public void itemStateChanged(Item item) 
    {
        try 
        {
            if (item != null) 
            {
                if (item == resolutions)
                {
                    trace("Current res " + camCtrl.getStillResolution());
                    camCtrl.setStillResolution(resolutions.getSelectedIndex());
                    trace("Current res " + camCtrl.getStillResolution());
                }
                else if (item == times)
                {
                    //String res[] = {"1/2sec", "1sec", "2sec", "5sec", "10sec", "20sec", "30sec", "1min", "2min", "5min"};
                    int tIndex = times.getSelectedIndex();
                    switch (tIndex) 
                    {
                        case 0: time = 500; break;
                        case 1: time = 1000; break;
                        case 2: time = 2000; break;
                        case 3: time = 5000; break;
                        case 4: time = 10000; break;
                        case 5: time = 20000; break;
                        case 6: time = 30000; break;
                        case 7: time = 60000; break;
                        case 8: time = 120000; break;
                        case 9: time = 300000; break;
                    }   
                    trace("Delay " + Integer.toString(time) + "ms");
                }               
                else if (item == exposureModes) 
                    camCtrl.setExposureMode(exposureModes.getString(exposureModes.getSelectedIndex()));
                else if (item == F_StopsCG) 
                {
                    expCtrl.setFStop(Integer.parseInt(F_StopsCG.getString(F_StopsCG.getSelectedIndex())));
                    //int fpStop = expCtrl.getFStop();
                    //int expTime = expCtrl.getExposureTime();
                    int EV = expCtrl.getExposureValue();
                    trace("The EV is: " + EV);
                } 
                else if (item == flashModes) 
                {
                    String mode = flashModes.getString(flashModes.getSelectedIndex());
                    for (int i = 0; i < flashAllModes.length; i++) 
                    {
                        if (mode.equals(flashAllModes[i])) 
                        {
                            flsCtrl.setMode(i + 1);
                            break;
                        }
                    }
                }
                else if(item == storageLocs)
                {
                    String loc = storageLocs.getString(storageLocs.getSelectedIndex());
                    trace("Changing storage to " + loc);
                    createPhotoDir(loc);
                }
                else if (item == focusModes) 
                {
                    try 
                    {
                        fcsCtrl.setFocus(focusModesInt[focusModes.getSelectedIndex()]);
                    } 
                    catch (MediaException me) 
                    {
                        me.printStackTrace();
                    }
                } 
                else  if (item == zoomDigModes) 
                    zoomCtrl.setDigitalZoom(zoomDigModesInt[zoomDigModes.getSelectedIndex()]);
                else  if (item == zoomOptModes) 
                    zoomCtrl.setOpticalZoom(zoomOptModesInt[zoomOptModes.getSelectedIndex()]);
            }
            display.setCurrent(vidForm);
        } 
        catch (Exception e) 
        {
            e.printStackTrace();
        }
    }

    
    private void startStopShooting() 
    {
        if(shooting)
        {
            trace("Stopping shooting");
            shooting = false;    
            vidForm.removeCommand(stopShootingCommand);
            vidForm.addCommand(startShootingCommand);
            return;
        }
        else
        {
            trace("Starting shooting");
            shooting = true;
            vidForm.removeCommand(startShootingCommand);
            vidForm.addCommand(stopShootingCommand);
        }
        
        new Thread() 
        {
            public void run() 
            {
               
                try 
                {
                    long stime = System.currentTimeMillis();
                    
                    FileConnection fconn = (FileConnection)Connector.open(PHOTOS_DIR + stime);
                    trace("Creating capture folder");
                    fconn.mkdir();
                    trace("Setting capture folder writeable");
                    fconn.setWritable(true);
                    trace("Setting capture folder readable");
                    fconn.setReadable(true);
                    trace("Capture folder created");
                    
                    snapCtrl.setFilePrefix(System.currentTimeMillis() + "_");
                    trace("Setting new snapshot folder: " + snapDir + stime + "/");
                    snapCtrl.setDirectory("/" + snapDir + stime + "/");                        
                    
                    while(shooting)
                    {
                        snapCtrl.start(1);
                        snapCtrl.unfreeze(true);
                        snapCtrl.stop();
                        Thread.sleep(time);
                     }
                }
                catch (Exception e) 
                {
                    e.printStackTrace();
                    System.exit(0);
                }

            }
        }.start();
    }
    
    private void enabledShutterFeedback(boolean enabledFeedback) 
    {
        final boolean bool = enabledFeedback;
        new Thread(new Runnable() 
        {
            public void run() 
            {
                try 
                {
                    camCtrl.enableShutterFeedback(bool);
                } 
                catch (Exception e) 
                {
                    e.printStackTrace();
                    return;
                } 
            }
        }).start();            
        if (enabledFeedback) 
        {
            vidForm.removeCommand(enabledShutterFeedbackCommand);
            vidForm.addCommand(disabledShutterFeedbackCommand);
        } 
        else 
        {
            vidForm.removeCommand(disabledShutterFeedbackCommand);
            vidForm.addCommand(enabledShutterFeedbackCommand);
        }
    }
         
    private void closePlayer() 
    {
        if (player != null) 
        {
            player.close();
            player = null;
        }
    }
}